// Vue.util.defineProperty(this, 'current','/')
function defineReactive(obj, key, val) {
  // 递归
  observe(val)

  const dep = new Dep()
  // 属性拦截
  Object.defineProperty(obj, key, {
    get() {
      // console.log('get', key)
      // 依赖收集建立
      Dep.target && dep.addDep(Dep.target)
      return val
    },
    set(v) {
      // if (v === val) return;
      // dep.notify()
      if (v !== val) {
        // console.log('set', key)
        // 传入新值v可能还是对象
        observe(v)
        val = v
        // 通知视图更新
        // update()
        // watchers.forEach(w=>w.update())
        dep.notify()
      }
    },
  })
}
// 遍历传入obj的所有属性，执行响应式处理
function observe(obj) {
  // 首先判断obj是对象
  if (typeof obj !== 'object' || obj == null) {
    return obj
  }
  Object.keys(obj).forEach(key => {
    defineReactive(obj, key, obj[key])
  })

}

// 拦截器
function proxy(vm) {
  // console.log(vm)
  Object.keys(vm.$data).forEach(key => {
    // 给每个数据设置响应式
    Object.defineProperty(vm, key, {
      get() {
        return vm.$data[key]
      },
      set(v) {
        vm.$data[key] = v
      },
    })
  })
}

class KVue {
  constructor(options) {
    // 0.保存选项
    this.$options = options
    this.$data = options.data
    // 1.响应式：递归遍历data中对象，做响应式处理
    observe(this.$data)
    // 1.5.代理
    proxy(this)
    // 2.编译模板
    new Compile(options.el, this)
  }
}

// 遍历模板树，解析其中动态部分，初始化并获得更新
class Compile {
  constructor(el, vm) {
    // 保存实例
    this.$vm = vm
    // 获取宿主元素的dom节点
    this.$el = document.querySelector(el)
    // 编译它
    this.compile(this.$el)
  }
  compile(el) {
    // 遍历
    // console.log(el)
    const childNodes = el.childNodes
    childNodes.forEach(node => {
      if (this.isElement(node)) {
        // 元素：解析动态的指令，属性绑定，事件
        // console.log('编译元素', node.nodeName)
        this.compileElement(node)
        // 递归
        if (node.childNodes && node.childNodes.length > 0) {
          this.compile(node)
        }
      } else if (this.isInter(node)) {
        // 插值绑定表达式
        // console.log('编译插值', node.textContent)
        this.compileText(node)
      }
    })
  }
  compileElement(node) {
    const attrs = node.attributes
    // console.log(attrs,node)
    // 遍历解析dom节点里面的 属性    name src ...
    Array.from(attrs).forEach(attr => {
      // 判断是否为一个动态属性 
      // 是否为以 ‘k-’ 开头的指令 
      const attrName = attr.name
      const exp = attr.value
      // console.log(exp, attrName)
      if (this.isDir(attrName)) {
        // 截取指令名
        const dir = attrName.substring(2)
        // console.log(attrName, exp, dir)
        // 看是否是合法指令，如果是则执行代码
        this[dir] && this[dir](node, exp)
      }
      if (this.isEvent(attrName)) {
        // console.log(attr.value)
        // const d = attr.value.split('(')
        // const event = d[0]
        // const value = d[1].split(')')[0]
        // console.log(event, value, this.$vm.$options.methods[event], this.$vm[value])
        // // this.$vm.$options.methods[event](value)
        // node.addEventListener('onclick', this.$vm.$options.methods[event](this.$vm[value]))
        // // 截取指令名
        // const dir = attrName.substring(1)
        // console.log(attrName, exp, dir)
        // // 看是否是合法指令，如果是则执行代码
        // this[dir] && this[dir](node, exp)
        // @click="onclick"
        const dir = attrName.substring(1)
        // 事件监听
        this.eventHandler(node, exp, dir)
      }
    })
  }
  // 提取
  // 处理所有动态绑定
  // dir 指的就是指令名称
  update(node, exp, dir) {
    // 1.初始化
    const fn = this[dir + 'Updater']
    fn && fn(node, this.$vm[exp])
    // 2.创建watcher实例，负责后续更新
    new Watcher(this.$vm, exp, function (val) {
      fn && fn(node, val)
    })
  }

  compileText(node) {
    // console.log(this.$vm, RegExp.$1, node.textContent)
    // RegExp.$1 正则第一个匹配到的值 $2第二个匹配的值
    // console.log(RegExp.$1)
    // node.textContent = this.$vm[RegExp.$1]
    this.update(node, RegExp.$1, 'text')
  }
  isDir(attrName) {
    // 如果这个属性以 k- 开头就是指令
    return attrName.startsWith('k-')
  }
  isEvent(attrName) {
    // 如果这个属性以 k- 开头就是指令
    return attrName.indexOf('@') === 0
  }
  isInter(node) {
    return node.nodeType === 3 && /\{\{(.*)\}\}/.test(node.textContent)
  }
  isElement(node) {
    return node.nodeType === 1
  }
  // k-text
  text(node, exp) {
    this.update(node, exp, 'text')
  }
  textUpdater(node, val) {
    node.textContent = val
  }
  // k-html
  html(node, exp) {
    this.update(node, exp, 'html')
  }
  htmlUpdater(node, val) {
    node.innerHTML = val
  }
  // k-model
  model(node, exp) {
    // console.log(exp,exp)
    this.update(node, exp, 'model')
    node.addEventListener('input', e => {
      this.$vm[exp] = e.target.value 
    })
  }
  modelUpdater(node, val) {
    node.value = val
  }
  // click
  click(node, val) {
    this.update(node, 'model')
  }
  clickUpdater(node, val) {
    node.value = val
  }
  eventHandler(node, exp, dir) {
    // node.addEventListener()
    // methods:{onClick:function(){}}
    const fn = this.$vm.$options.methods && this.$vm.$options.methods[exp]
    node.addEventListener(dir, fn.bind(this.$vm))
  }
}

// const watchers = []
// 负责具体节点更新
class Watcher {
  constructor(vm, key, updateFn) {
    this.vm = vm
    this.key = key
    this.updateFn = updateFn
    // 读取当前值，触发依赖手机
    Dep.target = this
    this.vm[this.key]
    Dep.target = null
    // watchers.push(this)
  }
  // Dep将来会调用update
  update() {
    // console.log(this.$vm)
    // const val = this.$vm[this.key]
    // this.updater.call(this.vm, val)
    this.updateFn.call(this.vm, this.vm[this.key])
  }
}
// Dep 和 响应式的属性key之间有一一对应关系
// 负责通知watchers更新
class Dep {
  constructor() {
    this.deps = []
  }
  addDep(dep) {
    this.deps.push(dep)
  }
  notify() {
    this.deps.forEach(dep => dep.update())
  }
}